﻿using Deployment.Data.Environment.Connection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace Deployment.Data.Environment
{
    [XmlRoot("Environment")]
    public class Environment : NotifyPropertyChanged
    {
        private string _name;
        private Boolean _isReplicated = false;
        private ObservableSortedList<AbstractConnection> _connections = new ObservableSortedList<AbstractConnection>(4, new ConnectionComparer());

        [XmlAttribute]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        [XmlAttribute]
        public bool IsReplicated
        {
            get { return _isReplicated; }
            set
            {
                if (_isReplicated != value)
                {
                    _isReplicated = value;
                    OnPropertyChanged("IsReplicated");
                }
            }
        }

        [XmlArray("Connections")]
        [XmlArrayItem("Connection", Type = typeof(ConnectionXmlSerializer<AbstractConnection>))]
        public ObservableSortedList<AbstractConnection> Connections
        {
            get { return _connections; }
        }

        public void AddConnection(AbstractConnection newConnection)
        {
            Type existingType = getConnectionTypeByName(newConnection.Name);
            if (existingType == null || Type.Equals(existingType, newConnection.GetType()))
            {
                _connections.Add(newConnection);
            }
            else
            {
                throw new ArgumentException("newConnection type of [" + newConnection.GetType().Name + "] cannot be added because an existing connection with the same name already exists and is of [" + existingType.Name + "] type.  Connections with the same name must share the same type as well.");
            }
        }

        public bool RemoveConnection(AbstractConnection connection)
        {
            return _connections.Remove(connection);
        }

        public bool RemoveConnection(string connectionName)
        {
            List<AbstractConnection> cons = getConnectionsByName(connectionName);
            bool failed = false;
            foreach (AbstractConnection con in cons)
            {
                if (!_connections.Remove(con))
                    failed = true;
            }
            return !failed;
        }

        public List<string> getDistinctConnectionNames()
        {
            List<String> names = new List<string>();
            foreach(AbstractConnection item in _connections){
                string conName = item.Name;
                bool found = false;
                foreach (string s in names)
                {
                    if (s.Equals(conName, StringComparison.CurrentCultureIgnoreCase))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                    names.Add(conName);
            }
            return names;
        }

        public List<AbstractConnection> getConnectionsByName(string name)
        {
            List<AbstractConnection> list = new List<AbstractConnection>();
            foreach (AbstractConnection con in _connections)
            {
                if (string.Equals(con.Name, name, StringComparison.CurrentCultureIgnoreCase))
                    list.Add(con);
                
            }
            return list;
        }

        private Type getConnectionTypeByName(string name){
            foreach(AbstractConnection con in _connections){
                if (string.Equals(con.Name, name, StringComparison.CurrentCultureIgnoreCase))
                    return con.GetType();
            }
            return null;
        }


        public List<AbstractConnection> getConnectionsByType(Type type)
        {
            List<AbstractConnection> list = new List<AbstractConnection>();
            foreach (AbstractConnection con in _connections)
            {
                if (con.GetType().Equals(type) || con.GetType().IsSubclassOf(type))
                    list.Add(con);
            }
            return list;
        }


        private class ConnectionComparer : IComparer<AbstractConnection>
        {

            public int Compare(AbstractConnection x, AbstractConnection y)
            {
                if (x == null && y == null)
                    return 0;
                if (x == null && y != null)
                    return -1;
                if (x != null && y == null)
                    return 1;

                return string.Compare(x.Name, y.Name, true);
            }
        }
        
    }
}
